package com.yanfan.data.quartz;

import com.alibaba.fastjson2.JSON;
import com.yanfan.base.service.ISessionStore;
import com.yanfan.base.session.Session;
import com.yanfan.common.core.mq.message.ModbusPollMsg;
import com.yanfan.common.core.thingsModel.ThingsModelSimpleItem;
import com.yanfan.common.enums.DeviceStatus;
import com.yanfan.common.utils.DateUtils;
import com.yanfan.common.utils.StringUtils;
import com.yanfan.common.utils.spring.SpringUtils;
import com.yanfan.iot.domain.*;
import com.yanfan.iot.enums.DeviceType;
import com.yanfan.iot.mapper.SceneMapper;
import com.yanfan.iot.model.Action;
import com.yanfan.iot.model.DeviceStatusVO;
import com.yanfan.iot.service.IDeviceService;
import com.yanfan.iot.service.IModbusJobService;
import com.yanfan.iot.service.IModbusParamsService;
import com.yanfan.mq.redischannel.producer.MessageProducer;
import com.yanfan.mq.ruleEngine.SceneContext;
import com.yanfan.mq.service.IDataHandler;
import com.yanfan.mq.service.IMqttMessagePublish;
import com.yanfan.mqtt.manager.MqttRemoteManager;
import com.yanfan.ruleEngine.core.FlowLogExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 任务执行工具
 *
 * @author kerwincui
 */
@Slf4j
public class JobInvokeUtil {

    /**
     * 获取消息推送接口
     */
    private static final IMqttMessagePublish messagePublish = SpringUtils.getBean(IMqttMessagePublish.class);

    private static final SceneMapper sceneMapper = SpringUtils.getBean(SceneMapper.class);

    private static final FlowLogExecutor flowExecutor = SpringUtils.getBean(FlowLogExecutor.class);

    private static final IDataHandler dataHandler = SpringUtils.getBean(IDataHandler.class);

    private static final IDeviceService deviceService = SpringUtils.getBean(IDeviceService.class);

    private static final IModbusJobService modbusJobService = SpringUtils.getBean(IModbusJobService.class);

    private static final IModbusParamsService modbusParamsService = SpringUtils.getBean(IModbusParamsService.class);

    private static final ISessionStore sessionStore = SpringUtils.getBean(ISessionStore.class);

    private static final MqttRemoteManager mqttRemoteManager = SpringUtils.getBean(MqttRemoteManager.class);

    /**
     * 执行方法
     *
     * @param deviceJob 系统任务
     */
    public static void invokeMethod(DeviceJob deviceJob) throws Exception {
        if (deviceJob.getJobType() == 1) {
            System.out.println("------------------------执行定时任务-----------------------------");
            List<Action> actions = JSON.parseArray(deviceJob.getActions(), Action.class);
            List<ThingsModelSimpleItem> propertys = new ArrayList<>();
            List<ThingsModelSimpleItem> functions = new ArrayList<>();
            for (int i = 0; i < actions.size(); i++) {
                ThingsModelSimpleItem model = new ThingsModelSimpleItem();
                model.setId(actions.get(i).getId());
                model.setValue(actions.get(i).getValue());
                model.setRemark("设备定时");
                if (actions.get(i).getType() == 1) {
                    propertys.add(model);
                } else if (actions.get(i).getType() == 2) {
                    functions.add(model);
                }
            }
            // 发布属性
            if (propertys.size() > 0) {
                messagePublish.publishProperty(deviceJob.getProductId(), deviceJob.getSerialNumber(), propertys, 0);
            }
            // 发布功能
            if (functions.size() > 0) {
                messagePublish.publishFunction(deviceJob.getProductId(), deviceJob.getSerialNumber(), functions, 0);
            }

        } else if (deviceJob.getJobType() == 3) {
            System.out.println("------------------[定时执行场景联动]---------------------");
            Scene scene = sceneMapper.selectSceneBySceneId(deviceJob.getSceneId());
            // 执行场景规则,异步非阻塞
            SceneContext context = new SceneContext("", 0L, 0, null);
            flowExecutor.execute2Future(String.valueOf(scene.getChainName()), null, context);
        } else if (4 == deviceJob.getJobType()) {
            System.out.println("------------------[定时执行场景运算型变量]---------------------");
            String s = dataHandler.calculateSceneModelTagValue(deviceJob.getDatasourceId());
            if (StringUtils.isEmpty(s)) {
                System.out.println("------------------[定时执行场景运算型变量失败：+" + s + "]---------------------");
            }
        } else if (5 == deviceJob.getJobType()) {
            log.info("----------------执行modbus轮训指令----------------------");
            Long jobId = deviceJob.getJobId();
            ModbusJob modbusJob = new ModbusJob();
            modbusJob.setJobId(jobId);
            modbusJob.setStatus("0");
            List<ModbusJob> modbusJobList = modbusJobService.selectModbusJobList(modbusJob);
            if (CollectionUtils.isEmpty(modbusJobList)) return;
            //处理子设备情况
            handleSubDeviceStatus(modbusJobList);
            List<String> commandList = modbusJobList.stream().map(ModbusJob::getCommand).collect(Collectors.toList());
            ModbusPollMsg modbusPollMsg = new ModbusPollMsg();
            modbusPollMsg.setSerialNumber(deviceJob.getSerialNumber());
            modbusPollMsg.setProductId(deviceJob.getProductId());
            modbusPollMsg.setCommandList(commandList);
            DeviceStatusVO deviceStatusVO = deviceService.selectDeviceStatusAndTransportStatus(modbusPollMsg.getSerialNumber());
            modbusPollMsg.setTransport(deviceStatusVO.getTransport());
            if (deviceStatusVO.getStatus() != DeviceStatus.ONLINE.getType()) {
                log.info("设备：[{}],不在线", modbusPollMsg.getSerialNumber());
                return;
            }
            log.info("执行modbus轮询指令:[{}]", JSON.toJSONString(commandList));
            MessageProducer.sendPropFetch(modbusPollMsg);
        }
    }

    /**
     * TODO --耗时操作，这里后续放在MQ处理
     * 处理子设备根据设备数据定时更新状态
     */
    public static void handleSubDeviceStatus(List<ModbusJob> modbusJobList) {
        for (ModbusJob modbusJob : modbusJobList) {
            String subSerialNumber = modbusJob.getSubSerialNumber();
            Session session = sessionStore.getSession(subSerialNumber);
            if (Objects.isNull(session)) continue;
            //如果是网关子设备，则检测子设备是否在特定时间内有数据上报
            if (modbusJob.getDeviceType() == DeviceType.SUB_GATEWAY.getCode()) {
                long deterTime = 300L; //这里默认使用5分钟,如果轮询时间大于5分钟，请在配置参数设置更长时间
                ModbusParams params = modbusParamsService.getModbusParamsByDeviceId(modbusJob.getSubDeviceId());
                if (!Objects.isNull(params)) {
                    deterTime = Long.parseLong(params.getDeterTimer());
                }

                long lastAccessTime = session.getLastAccessTime();
                //如果现在的时间 - 最后访问时间 > 判断时间则更新为离线
                long time = (System.currentTimeMillis() - lastAccessTime) / 1000;
                if (time > deterTime) {
                    //处理设备离线
                    Device updateBo = new Device();
                    updateBo.setStatus(DeviceStatus.OFFLINE.getType());
                    updateBo.setSerialNumber(subSerialNumber);
                    updateBo.setUpdateTime(DateUtils.getNowDate());
                    deviceService.updateDeviceStatus(updateBo);
                    mqttRemoteManager.pushDeviceStatus(params.getProductId(), subSerialNumber, DeviceStatus.OFFLINE);
                    sessionStore.cleanSession(subSerialNumber);
                    log.info("设备[{}],超过：[{}],未上报数据，更新为离线", subSerialNumber, deterTime);
                }

            }
        }
    }

}
